#include <iostream>
#include <string>
#include <vector>

// 管道头文件：
// #include <unistd.h>
// fork头文件：
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

#include "Task.hpp"
#include <sys/wait.h>

// void work(int rfd)
// {
//     while (true)
//     {
//         int command = 0;
//         // 读上父进程0给我写的整数赋给command
//         int n = read(rfd, &command, sizeof(command));
//         if (n == sizeof(int))
//         {
//             std::cout << "pid is : " << getpid() << ", header task" << std::endl;
//             ExcuteTask(command);
//         }
//         else if (n == 0)
//         {
//             std::cout << "sub process : " << getpid() << " quit" << std::endl;
//             break;
//         }
//     }
// }



class Channel // 信道
{
public:
    Channel(int wfd, pid_t id, const std::string &name)
        : _wfd(wfd), _subprocessid(id), _name(name)
    {
    }
    int GetWfd() { return _wfd; }
    pid_t GetId() { return _subprocessid; }
    std::string GetName() { return _name; }
    void Wait()
    {
        pid_t rid = waitpid(_subprocessid, nullptr, 0); // 不关心退出码
        if (rid > 0)
        {
            std::cout << "father wait: " << rid << " success!" << std::endl;
        }
    }
    void CloseChannel()
    {
        if (_wfd >= 0)
            close(_wfd);
    }
    ~Channel()
    {
    }

private:
    int _wfd;
    pid_t _subprocessid; // 子进程id
    std::string _name;   // 给对应的信道取名字
};
// 在往后：形参类型和命名规范：
// const& 表示输出型参数
//& ：输入输出型参数
//* : 输出型参数



// 1.创建子进程和进程池
void CreateChannelAndSub(int num, std::vector<Channel> *channels, task_t task)
{
    // 创建子进程和进程池
    for (int i = 0; i < num; i++)
    {
        // 2.创建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        if (n < 0)
            exit(1);


        // 3.创建子进程
        pid_t id = fork();
        if (id == 0)
        {
            if(!channels->empty())
            {
                //已经是创建了第>=2个管道 此时的子进程拷贝过来的channels会保存着之前父进程与子进程通信的管道的写端
                //关闭掉拷贝过来的写端
                for (auto &channel : *channels)
                {
                    channel.CloseChannel();
                }
            }
            // child --read
            close(pipefd[1]); // 关闭子进程的写
            dup2(pipefd[0], 0); //将管道的读端，重定向到标准输入
            task();  // 让子进程去工作，退出循环
            close(pipefd[0]);//关闭了子进程的读端
            exit(0);
        }
        // 构建子进程的名字
        std::string channel_name = "Channel-" + std::to_string(i);

        // father
        close(pipefd[0]); //父进程关闭了读端，此时的父进程还有写端

        // a.子进程的pid b.父进程关心的管道写入端
        channels->push_back(Channel(pipefd[1], id, channel_name));

        // 继续创建其他子进程
    }

}

// 选择信道 -- 拿下标
int NextChannel(int channelnum)
{
    static int next = 0; // 初始化用next
    int channel = next;  // 默认channel为0
    next++;
    next %= channelnum; // 防止下标越界，随着函数不断被调用，形成一个循环使用子进程
    return channel;
}

void SendTaskCommand(Channel &channel, int taskcommand)
{
    // 通过write向指定的文件描述符中发送任务函数指针数组的下标
    write(channel.GetWfd(), &taskcommand, sizeof(taskcommand));
}
void ControlProOnce(std::vector<Channel> &channels)
{
    sleep(1);
    // a.选择一个任务
    int taskcommand = SelectTask();

    // b.选择一个进程
    int channel_index = NextChannel(channels.size());

    // c.发送任务 -- 使用哪一个信道--子进程，来完成哪个任务
    SendTaskCommand(channels[channel_index], taskcommand);
    std::cout << std::endl;
    std::cout << "taskcommand: " << taskcommand << ", channel: "
              << channels[channel_index].GetName() << ", sub process: " << channels[channel_index].GetId()
              << std::endl;
}
void ControlPro(std::vector<Channel> &channels, int times = -1)
{
    if (times > 0)
    {
        while(times--)
        {
            ControlProOnce(channels);
        }
    }
    else
    {
        while (true)
        {
            ControlProOnce(channels);
        }
    }
}

void ClearUpChannel(std::vector<Channel> &channels)
{
    // for (auto &channel : channels)
    // {
    //     channel.CloseChannel();
    // }
    // for (auto &channel : channels)
    // {
    //     channel.Wait();
    // }

    for (auto &channel : channels)
    {
        channel.CloseChannel();
        channel.Wait();
    }
}

// 能够做到：命令行参数两个
//./processpool 5 创建5个子进程

int main(int argc, char *argv[])
{
    // 命令行参数不是2个
    if (argc != 2)
    {
        std::cerr << "Usage: " << argv[0] << " processnum" << std::endl;
        return 1;
    }
    // 通过命令行输出的子进程个数来创建几个子进程
    int num = std::stoi(argv[1]); // 将命令行所记录的第二个参数赋值给num
    // 加载任务
    LoadTask();
    std::vector<Channel> channels; // 将对信道的管理，转换成对vector的增删查改
    // 1.创建子进程和进程池
    CreateChannelAndSub(num, &channels, work);

    // 2.通过信道来控制对应的子进程  
    ControlPro(channels, 5);

    // 3.回收管道和子进程  a.关闭所有的写端 b.回收子进程
    ClearUpChannel(channels);

    // 注意：

    // // for test
    // for (auto &channel : channels)
    // {
    //     std::cout << "------------------------------------------" << std::endl;
    //     std::cout << channel.GetName() << std::endl;
    //     std::cout << channel.GetWfd() << std::endl;
    //     std::cout << channel.GetId() << std::endl;
    // }

    // sleep(100);
    return 0;
}